import axios from 'axios'
import qs from 'query-string'
import Cookie from 'js-cookie'
// import Store from '@wattjs/store'
import getFingerPrint from './finger.js'

/**
 * nodejs等服务器环境
 */
//  import SsrNetwork from './ssr_network'

const ser = Symbol('ser')
class Fetch {
  constructor({
    headers,
    withCredentials = false,
    config = {},
    transformRequest = [],
    transformResponse = [],
    showToast,
    afterCallback
  }) {
    this.networkList = null
    this.clientId = null
    this.config = config
    this.headers = headers
    this.showToast = showToast // 错误提示
    this.withCredentials = withCredentials // 是否带cookie
    this.transformRequest = transformRequest // 请求前参数处理
    this.transformResponse = transformResponse // 请求后参数处理
    this.afterCallback = afterCallback

    this.created() // 初始化实例
  }

  async created() {
    const {
      BASE_URL,
      REQUEST_TIMEOUT,
    } = this.config
    // config设置全局
    // Store.set('config', this.config)
    // 默认请求实例 可改变
    this.fetch = axios.create({
      baseURL: BASE_URL,
      timeout: REQUEST_TIMEOUT || 4000,
    })
    this.networkList = [] // 请求列表容器
    // await this.getClientId()
  }

  // 表单数据格式化
  [ser](obj) {
    const form = new FormData()
    for (const key in obj) {
      if ({}.hasOwnProperty.call(obj, key)) {
        // 数组处理
        if (Object.prototype.toString.call(obj[key]) === '[object Array]') {
          obj[key].forEach(ret => {
            form.append(key, ret)
          })
        } else form.append(key, obj[key])
      }
    }
    return form
  }

  // 取设备ID 可服务端
  async getClientId() {
    if (typeof this.clientId === 'string') return this.clientId
    this.clientId = await getFingerPrint()
    return this.clientId
  }

  async init(option) {
    const { APP_ID, XSRF_HEADER } = this.config
    let { headers } = option
    headers = Object.assign({}, {
      'CLIENT-ID': await this.getClientId(), // 取设备ID
      'Content-Type': 'application/json',
      'X-Requested-With': 'XMLHttpRequest',
      'X-XSRF-TOKEN': XSRF_HEADER,
      'APP-ID': APP_ID
    }, this.headers, headers)
    delete option.headers

    // const isNode = Object.prototype.toString.call(process) === '[object process]'
    const isServer = !!globalThis.ctx
    const isBrowser = !!globalThis.location
    if (isBrowser) {
      const { href, pathname, search } = window.location
      const { userAgentData = {}, connection = {} } = window.navigator
      const { width, height } = window.screen

      // Referer Host Origin User-Agent 已存在
      headers = Object.assign({
        'TICKET-ID': Cookie.get(XSRF_HEADER),
        'APP-PLATFORM': userAgentData.platform,
        'SCREEN-WIDTH': width,
        'SCREEN-HEIGHT': height,
        'NETWORK-TYPE': ['wifi', 'ethernet'].includes(connection.type)?connection.type:connection.effectiveType,
        'WEB-URL': href,
        'WEB-PATH': pathname,
        'WEB-SEARCH': search,
      }, headers)
    } else if (isServer) {
      const { req, $config } = globalThis.ctx
      headers['APP-PLATFORM'] = 'server'
      headers = Object.assign({
        'Cookie': req.headers.cookie,
        'Referer': req.headers.referer,
        'Host': req.headers.host,
        'Origin': 'https://' + req.headers.host,
        'USER-AGENT': req.headers['user-agent']
      }, headers)
    } else {
      headers = Object.assign({
        'APP-PLATFORM': 'node'
      }, headers)
    }

    const fmtRequest = [(data, headers) => {
      // 在向服务器发送前，修改请求数据
      if (headers['Content-Type'] === 'application/json') {
        data = JSON.stringify(data)
      } else if (headers['Content-Type'] === 'application/x-www-form-urlencoded') {
        data = qs.stringify(data, {arrayFormat: 'bracket'})
      } else if (headers['Content-Type'] === 'multipart/form-data') {
        if (isBrowser) data = this[ser](data)
      }
      return data
    }]
    const fmtResponse = [(data, headers) => {
      // console.log(headers, 'headers')
      // 传递给 then/catch 前，修改响应数据
      return data
    }]

    const defOptions = {
      transformRequest: [...this.transformRequest, ...fmtRequest],
      transformResponse: [...fmtResponse, ...this.transformResponse],
      headers,
      showToast: this.showToast,
      withCredentials: this.withCredentials,
    }
    return Object.assign({}, defOptions, option)
  }

  getRealUrl(option) {
    const { url, prefix } = option
    return /^(http|https):\/\//.test(url) ? url : `${prefix}${url}`
  }

  async request({
    prefix = '',
    url = '',
    data = {},
    params = {},
    method = 'GET',
    headers = {},
    timeout = 10000,
    signal = '',
    errToast = true, // 是否显示错误
    canRepeat = false, // 是否可以重复请求
    needJudgeResContent = true, // 是否只返回response.data
    withCredentials,
    responseType = 'json',
    callback
  }) {
    const rurl = this.getRealUrl({ url, prefix })
    const option = await this.init({
      url: rurl,
      data,
      params,
      method,
      headers,
      timeout,
      signal,
      errToast,
      canRepeat,
      needJudgeResContent,
      responseType,
      callback
    })
    typeof withCredentials === 'boolean' && (option.withCredentials = withCredentials)
    return this.fetch.request(option)
  }

  async handleBack(response) {
    const { status, data, config } = response
    const { callback, needJudgeResContent } = config
    const { RES_CODE = [0, 200] } = this.config

    // HTTP状态码不正确
    if (status !== 200) throw response
    else {
      const Serialization = typeof data === 'string' && data.indexOf('{') > -1
      const body = Serialization ? JSON.parse(data) : data

      // || body.data === undefined
      if (needJudgeResContent && (body.code === undefined || body.msg === undefined)) {
        body.msg = '返回格式错误！'
        response.data = JSON.stringify(body)
        throw response
      }

      // 自定义请求后格式化
      if (this.afterCallback && typeof this.afterCallback === 'function') {
        return this.afterCallback(body)
      }
      if (callback && typeof callback === 'function') {
        callback(body)
      }
      else if (!needJudgeResContent) return body
      else if (!RES_CODE.includes(body.code)) throw response
      else return body.data
    }
  }

  // 请求错误
  handleError(response) {
    const { code, msg, status, data, config } = response
    const Serialization = typeof data === 'string' && data.indexOf('{') > -1
    const body = Serialization ? JSON.parse(data) : data

    // 自定义错误
    if (code && code < 0 && msg) {
      typeof this.showToast === 'function' && this.showToast(msg)
      return
    }

    // 取消不报错
    if (status === undefined) return

    let contentMsg = ''
    switch (status) {
      case 200:
        body.msg && (contentMsg = body.msg)
        break
      case 403:
        contentMsg = '服务器拒绝请求！'
        break
      case 404:
        contentMsg = '服务器找不到请求！'
        break
      case 408:
        contentMsg = '网络已超时，请重试！'
        break
      case 500:
        contentMsg = '服务器出错了！'
        break
      case 502:
        contentMsg = '服务器跑路了⊙﹏⊙！'
        break
      case 503:
        contentMsg = '服务器宕机了-_-。sorry！'
        break
      case 504:
        contentMsg = '服务器没有反应了！'
        break
      default:
        contentMsg = '网络错误! 请重试'
        break
    }

    // toast 提示接口错误
    if (!(config && !config.errToast) && this.showToast) {
      typeof this.showToast === 'function' && this.showToast(contentMsg)
    }
  }

  handleFinally(option) {
    if (!option) return
    const { url, prefix, method } = option
    const rurl = this.getRealUrl({ url, prefix })
    const flag = method.toLowerCase() + rurl
    // 完成清除标记
    const index = this.networkList.findIndex(r => r === flag)
    index > -1 && this.networkList.splice(index, 1)
  }
}

export default Fetch
